package net.godideas.giframework.model.api;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import net.godideas.giframework.annotation.ApiParam;
import net.godideas.giframework.annotation.ApiParam.Type;
import net.godideas.giframework.annotation.ApiRequest;
import net.godideas.giframework.annotation.ApiUrl;

import com.android.volley.AuthFailureError;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.toolbox.StringRequest;

public class ApiExecutor<T> {
	private T mRequest;

	public T getRequest() {return mRequest;}
	
	public ApiExecutor(T request) {
		mRequest = request;
	}

	public void execute(RequestQueue queue,
			Response.Listener<String> successListener,
			Response.ErrorListener errorListener) {
		// Parse method and url
		ApiRequest apiRequest = (ApiRequest) mRequest.getClass().getAnnotation(
				ApiRequest.class);
		// Parse param
		final HashMap<String, String> headerParams = new HashMap<String, String>();
		final HashMap<String, String> formParams = new HashMap<String, String>();
		TreeMap<Integer, String> queryParams = new TreeMap<Integer, String>();
		
		ArrayList<Field> allFields = new ArrayList<Field>();
		getAllFields(mRequest.getClass(), allFields);
		for (Field field : allFields) {
			if (field.isAnnotationPresent(ApiParam.class)) {
				ApiParam apiParam = (ApiParam) field
						.getAnnotation(ApiParam.class);

				if (apiParam.type() == Type.HEADER)
					try {
						field.setAccessible(true);
						headerParams.put(apiParam.key(),
								String.valueOf(field.get(mRequest)));
					} catch (Exception e) {
						e.printStackTrace();
					}
				if (apiParam.type() == Type.FORM)
					try {
						formParams.put(apiParam.key(),
								String.valueOf(field.get(mRequest)));
					} catch (Exception e) {
						e.printStackTrace();
					}
				if (apiParam.type() == Type.QUERY)
					try {
						queryParams.put(apiParam.order(),
								String.valueOf(field.get(mRequest)));
					} catch (Exception e) {
						e.printStackTrace();
					}
			}
		}
		// Build url
		String url = apiRequest.url();
		if (queryParams.size() > 0) {
			for (int i = 0; i < queryParams.size(); i++) {
				url = String.format(url, queryParams);
			}
		}
		// Parse url
		if (url.equals("")) {
			ArrayList<Method> allMethods = new ArrayList<Method>();
			getAllMethods(mRequest.getClass(), allMethods);
			for (Method method : mRequest.getClass().getMethods()) {
				if (method.isAnnotationPresent(ApiUrl.class)) {
					try {
						method.setAccessible(true);
						url = (String) method.invoke(mRequest);
					} catch (Exception e) {
						e.printStackTrace();
					}
					break;
				}
			}
		}
		
		StringRequest request = new StringRequest(apiRequest.method(), url,
				successListener, errorListener) {
			@Override
			public Map<String, String> getHeaders() throws AuthFailureError {
				return headerParams;
			}

			@Override
			protected Map<String, String> getParams() throws AuthFailureError {
				return formParams;
			}
		};

		queue.add(request);
	}

	private List<Field> getAllFields(Class<?> clazz, List<Field> list) {
		Class<?> superClazz = clazz.getSuperclass();
	    if(superClazz != null){
	    	getAllFields(superClazz, list);
	    }
	    list.addAll(Arrays.asList(clazz.getDeclaredFields()));
	    return list;
	}
	
	private List<Method> getAllMethods(Class<?> clazz, List<Method> list) {
	    Class<?> superClazz = clazz.getSuperclass();
	    if(superClazz != null){
	    	getAllMethods(superClazz, list);
	    }
	    list.addAll(Arrays.asList(clazz.getDeclaredMethods()));
	    return list;
	}
}
